package main

// <-------------------------------- 测试部分 ------------------------------------------------>
import (
	"database/sql"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"

	_ "github.com/go-sql-driver/mysql"
)

/*
**	功能：入口函数，
**	参数：1. res 回复
**		 2. req  请求
 */
func entry(res http.ResponseWriter, req *http.Request) {

	bodyContent, err := ioutil.ReadAll(req.Body)
	if err != nil && err != io.EOF {
		fmt.Printf("read body content failed, err:[%s]\n", err.Error())
		return
	}

	// 将数据打包成job结构体
	work := Job{serload: Serload{bodyContent: bodyContent, dbs: dbs}}

	select {
	// 将打包好的任务放入通道，等待协程（自动）获取
	case JobQueue <- work:
	case <-time.After(time.Second * 20): // 三秒超时
		fmt.Fprintf(res, "timeout!")
		fmt.Println("Time out")
	}

	// 回复请求的一种用法，直接向res写入数据
	fmt.Fprintf(res, "hello world")
}

/*
	作用：协程的工作函数，包含了协程的所有行为：访问数据库，校验sign，转发
	参数：1. bodyContent 请求发来的json
		 2. db	数据库连接池

*/
func CoroutineWork(bodyContent []byte, db *sql.DB) {
	var appid string   // 	类似于id一样的识别
	var method string  //	转发的通道（还不确定）
	var oldSign string //	发来的信息签名

	var sql1 string //	查询platform表
	var sql2 string //	查询methods表

	var appsecret string  //	platform表中匹配appid的密钥 （用于和排好序的信息拼接 签名）
	var appmethods string //	platform表中appid用户的访问方式权限 （例如：转发通道method必须满足 esrple.* ）

	var http_url string    //	转发的地址
	var http_verb string   //	转发的方式 （例如：POST 或者 ampq）
	var http_mime string   //	转发的格式 （例如：json）
	var rpc_exclude string //	需要排除的json信息
	var resp_type string   //	收到回复的方式 （例如：返回包装的数据信息，或者不要包装，本代码中目前没使用）

	// <------------------------- 完成平台密钥和json文件的融合 ----------------------------->

	result, sendMap, myerr := read_json(bodyContent)
	if myerr != nil {
		fmt.Println("read_json is wrong: ", myerr)
		return
	}
	appid = sendMap["appid"]
	method = sendMap["method"]
	oldSign = sendMap["sign"]

	db.Ping()

	sql1 = "select appsecret,appmethods from platforms where appid=?"
	rows1, err := sql_query(db, sql1, appid)
	if err != nil {
		fmt.Println("查询失败", err)
		return
	}
	for rows1.Next() {
		//把行内值付给变量
		rows1.Scan(&appsecret, &appmethods)
	}
	defer func() {
		if rows1 != nil {
			rows1.Close()
			//	fmt.Println("关闭结果集1")
		}
	}()

	sign := Hmac(appsecret, result)  //	<------------------hmac_md5加密，合成之后的sign
	newSign := strings.ToUpper(sign) //  将合成之后的sign转换成大写

	fmt.Println("new", " ", newSign) // 打印生成的sign

	if newSign != oldSign {
		fmt.Println("sign 匹配失败")
		return
	}

	appid_method := strings.Replace(appmethods, "*", "", -1)

	if strings.Contains(method, appid_method) == false {
		fmt.Println("无法访问methods表，因为method与appmethods不匹配")
		return
	}

	// <------------------------- 完成获取转发路径的方法 -------------------------------------->

	sql2 = "select http_url,http_verb,http_mime, rpc_exclude, resp_type from methods where method = ?"
	rows2, err := sql_query(db, sql2, method)
	if err != nil {
		fmt.Println("第二个库查询失败", err)
		return
	}
	for rows2.Next() {
		//把行内值付给变量
		rows2.Scan(&http_url, &http_verb, &http_mime, &rpc_exclude, &resp_type) // <----------- 获取多行 转发地址
		//fmt.Println("methods表 获取的数据 : ", http_url, http_verb, http_mime)
	}
	defer func() {
		if rows2 != nil {
			rows2.Close()
			//	fmt.Println("关闭结果集2")
		}
	}()

	// <--------------------------------  对收到的信息(已经转成map)进行删减 ------------------------------------>
	map_user_del := make(map[string]string)
	for k, _ := range sendMap {
		map_user_del[k] = sendMap[k]
	}

	arr := strings.Split(rpc_exclude, ",")
	for index := range arr {
		delete(map_user_del, arr[index])
	}

	// <-------------------------------------- 转发 -------------------------------------------->
	switch http_verb {
	case "POST":
		HttpWork(http_url, http_verb, http_mime, map_user_del)
	case "amqp":
		AmqpWork(http_url, map_user_del)
	default:
		return
	}
}

/*
	功能：各种初始化（程序默认先运行的函数）
*/
func init() {
	runtime.GOMAXPROCS(MaxWorker)
	JobQueue = make(chan Job, MaxQueue)    // 任务通道初始化
	dispatcher := NewDispatcher(MaxWorker) // 适配器初始化
	dispatcher.Run()                       // 启动适配器

	var myerr error
	dbs, myerr = sql.Open("mysql", "sifeiesl:sifeiesl@tcp(172.18.130.2:3306)/tran")
	if myerr != nil {
		fmt.Println("数据库连接失败")
		return
	}
	//dbs.SetMaxOpenConns(100) // 限制连接数
	fmt.Println("数据库连接成功")

}

func main() {
	Port := "8086"
	IsHttp := true
	arg_num := len(os.Args)
	if 2 <= arg_num {
		Port = os.Args[1]
	}

	if 3 <= arg_num {
		if os.Args[2] == "true" {
			IsHttp = true
		} else {
			IsHttp = false
		}
	}
	fmt.Printf("server is http %t\n", IsHttp)
	fmt.Println("server listens at ", Port)
	fmt.Println()

	http.HandleFunc("/", entry)

	var err error
	if IsHttp {
		err = http.ListenAndServe(":"+Port, nil)
	} else {
		err = http.ListenAndServeTLS(":"+Port, "server.crt", "server.key", nil)
	}
	if err != nil {
		fmt.Println("Server failure /// ", err)
	}
	//关闭数据库连接
	defer func() {
		if dbs != nil {
			dbs.Close()
			fmt.Println("关闭连接")
		}
	}()
	fmt.Println("quit")
}
